﻿// Spun AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Spun.Models
{
    public class ModelBase
    {
        public delegate void MemberChanged(ModelBase Model, string MemberName, object OldValue, object NewValue);

        public event MemberChanged MemberChangedEvent;

        public ModelBase(string ModelClass, IEnumerable<ModelMember> Members)
        {
            this.Members = new Dictionary<string, ModelMember>();
            MemberValues = new Dictionary<string, object>();

            modelClass = ModelClass;

            foreach (var NewMember in Members)
            {
                if (this.Members.ContainsKey(NewMember.Name))
                    throw new DuplicateMember(ModelClass, NewMember.Name);
                else
                    this.Members[NewMember.Name] = NewMember;
            }
        }

        public string ModelClass
        {
            get { return modelClass; }
        }

        public IDictionary<string, object> AllMembersValues
        {
            get
            {
                var AllMembersValues = new Dictionary<string, object>();

                foreach (var Member in Members)
                {
                    if (MemberValues.ContainsKey(Member.Key))
                        AllMembersValues[Member.Key] = MemberValues[Member.Key];
                    else
                        AllMembersValues[Member.Key] = null;
                }

                return AllMembersValues;
            }
        }

        public void Update<T>(string MemberName, T Value)
        {
            object OldValue;

            if (MemberValues.ContainsKey(MemberName))
                OldValue = MemberValues[MemberName];
            else
                OldValue = null;

            var Member = GetMember(MemberName);

            if (Value != null)
                if (Value is T == false)
                    throw new TypeMismatch(ModelClass, typeof(T), Value.GetType());

            MemberValues[MemberName] = Value;

            OnMemberChangedEvent(this, MemberName, OldValue, Value);
        }

        public T Get<T>(string MemberName)
        {
            var Member = GetMember(MemberName);

            object Value = null;

            if (MemberValues.TryGetValue(MemberName, out Value))
            {
                if (Value == null)
                {
                    return default(T);
                }
                else
                {
                    if (Value is T == false)
                        throw new TypeMismatch(ModelClass, typeof(T), Value.GetType());
                    else
                        return (T)Value;
                }
            }
            else
            {
                return default(T);
            }
        }

        public Type GetMemberType(string MemberName)
        {
            ModelMember ModelMember;

            if (Members.TryGetValue(MemberName, out ModelMember))
                return ModelMember.Type;
            else
                throw new MemberNotFound(ModelClass, MemberName);
        }

        protected void AddMember(ModelMember Member)
        {
            if (Members.ContainsKey(Member.Name))
                throw new DuplicateMember(ModelClass, Member.Name);
            else
                Members[Member.Name] = Member;
        }

        protected void RemoveMember(ModelMember Member)
        {
            if (Members.ContainsKey(Member.Name) == false)
                throw new MemberNotFound(ModelClass, Member.Name);
            else
                Members.Remove(Member.Name);
        }

        private void OnMemberChangedEvent(ModelBase Model, string MemberName, object OldValue, object NewValue)
        {
            if (MemberChangedEvent != null)
                MemberChangedEvent(Model, MemberName, OldValue, NewValue);
        }

        private ModelMember GetMember(string MemberName)
        {
            ModelMember Member;

            if (Members.TryGetValue(MemberName, out Member))
            {
                return Member;
            }
            else
            {
                throw new MemberNotFound(ModelClass, MemberName);
            }
        }

        private string modelClass;
        private Dictionary<string, ModelMember> Members;
        private Dictionary<string, object> MemberValues;
    }
}
